数据结构(C):以临接矩阵存和邻接表存储的无(有)向图的深度和广度遍历(附完整代码)

1.深度优先遍历

思想:
①从图中某个顶点V0 出发,访问此顶点。
②依次从V0的各个未被访问的邻接点出发深度优先遍历图, 直至图中所有和V0有路径相通的顶点都被访问到。
③若图中还有顶点未被访问(非连通图),另选图中一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
在这里插入图片描述
注:虽然由图形得出的深度遍历序列不唯一,但是对于给定的存储结构深度遍历序列唯一。如:在这里插入图片描述
在遍历图时,对图中每个顶点至多调用一次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。
遍历图的实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。
用邻接矩阵存储图时,查找每个顶点的邻接点需要O(n2);//找每个点的邻接点都要到对应的行去遍历一遍
用邻接表存储图时,查找每个顶点的邻接点需要O(e);//只访问了每条边
n—图中顶点的个数;
e—边数/弧数
当以邻接表作存储结构时,深度优先遍历的时间复杂度为O(n+e)//初始化+遍历
矩阵的话是O(n+n2)=O(n2)
另外在后面的求拓扑排序的序列时也可以借助深度优先遍历得到

2. 广度优先遍历

思想:
①从图中的某个顶点v0出发,访问此顶点,
②依次访问v0的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和v0有路径相通的顶点都被访问到。
③若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
在这里插入图片描述
时间复杂度深度优先遍历。

代码

有向和无向图的区别就是,在建立邻接矩阵或者邻接表时,前者一次建立两条对称关联,后者只建立一条,稍作改动即可相互转换。而网和图的区别,前者把权值全赋值为0,后者用户自己输入,这里以无向图为例
里面用到了用户自定义头文件,如果不知道怎么建立,请参照如何建立和调用用户自定义的头文件

 #include<stdio.h>
#include<stdlib.h>
#include<limits.h>//包含c语言里的最大值INT_MAX最小值INT_MIN 
#define INFINITY INT_MAX//INT_MAX=2^31-1 
#define MAX_VERTEX_NUM 20
#define MAX 100
#define ERROR 0
#define OVERFLOW 0
#define TRUE 1
#define FALSE 0
#define OK 1
typedef int VRType ;
typedef int InfoType ;
typedef  char VertexType ;//顶点的标识类型
typedef  int Status;
typedef enum {DG,DN,UDG,UDN} GraphKind;//省略枚举类型变量名 
typedef struct ArcCell{
	VRType adj; //VRType是定点关系类型,对无权图,用0或1;对带权图表示权值类型
	InfoType *info;//表示该弧相关信息的指针 
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
//临接矩阵的二维数组定义
#include"queuevex.h"
typedef struct{
   VertexType vexs[MAX_VERTEX_NUM];   //顶点向量
   AdjMatrix arcs;                    //邻接矩阵 
   int vexnum,arcnum;                 //图的当前顶点数和弧度 
   int kind;                    //图的种类标志 
}MGraph; //kind这种的图的所有信息的定义


Status CreatUDN(MGraph &G);

Status CreatGraph(MGraph &G)
{
	printf("please input the kind of graph\n");
	scanf("%d", &G.kind);
	switch(G.kind)
	{
//		case DG:return  CreatDG(G);//构造有向图
//		case DN:return  CreatDN(G);//构造无向图
//		case UDG:return CreatUDG(G);//构造无向网
		case UDN:return CreatUDN(G);//构造无向网
     	default: return ERROR;
	}
		
}
	
int LocateVex(MGraph &G,char v)
{  for(int i=0;i<G.vexnum;i++)
          if(G.vexs[i]==v)
   return i;
}


Status CreatUDN(MGraph &G) //在邻接矩阵存储结构上,构造无向网G
{
	char v1,v2;
	int  w;
    printf("请输入无向网的顶点数和边数");
    scanf("%d%d",&G.vexnum,&G.arcnum);//读入顶点和边数目
	printf("请按顺序输入节点标识符");
	for(int i=0;i<G.vexnum;i++)
	   scanf(" %c",&G.vexs[i]); //构造顶点向量,用来表示行坐标或者列坐标,因为临接矩阵是没有位置来存表头的
	   printf("%c%c",G.vexs[0],G.vexs[1]);
	for(int i=0;i<G.vexnum;i++) //邻接矩阵初始化
	   for(int j=0;j<G.vexnum;j++)
	   {
		G.arcs[i][j]={INFINITY,NULL};

	for(int k=0;k<G.arcnum;k++)//构造邻接矩阵
	{
        printf("请依次输入每条边依附的顶点和权值");
	    scanf(" %c %c %d",&v1,&v2,&w); //读入一条边依附的顶点及权值
		int i=LocateVex(G,v1);
		int j=LocateVex(G,v2);//确定v1、v2在图中的位置
		G.arcs[i][j].adj=w;//边<v1,v2>的权值
		G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称弧<v2,v1 >
	} 	return OK;
      }
	  }//CreateUDN


void PrintMap(MGraph G)
{
	int i=0,j=0;
	for(i=0;i<G.vexnum;i++)
	  {
	  	printf("%5c",G.vexs[i]);
	  	for(j=0;j<G.vexnum;j++)
		   {if(G.arcs[i][j].adj==INFINITY)
             printf("%5s","oo");
			else printf("%5d",G.arcs[i][j].adj);
			}
			printf("\n");
      }
}

int visited[MAX];

//邻接矩阵的深度优先遍历
void DFS(MGraph G, int i)
{
    int j;
    visited[i] = TRUE;
    printf("%c ", G.vexs[i]);
    for (j=0; j<G.vexnum; ++j)
    {
        if (G.arcs[i][j].adj!=INFINITY && !visited[j])
            DFS(G, j);
    }
}

void DFSTraverse(MGraph G)
{
    int i;
    for (i=0; i<G.vexnum; i++)
        visited[i] = FALSE;

    for (i=0; i<G.vexnum; i++)
    {
        if (!visited[i])
            DFS(G, i);
    }

}

//邻接矩阵的广度优先遍历
void BFSTraverse(MGraph &G)
{
    int i, j;
    SqQueue Q;

    for (i=0; i<G.vexnum; ++i)
        visited[i] = FALSE;

    InitQueue(Q);

    for (i=0; i<G.vexnum; ++i)
    {
        if (!visited[i])
        {
            printf("%c", G.vexs[i]);
			visited[i] = TRUE;
            EnQueue(Q, i);

            while (!QueueEmpty(Q))
            {
                DeQueue(Q, i);
                for (j=0; j<G.vexnum; ++j)
                {
                    if (!visited[j] && G.arcs[i][j].adj!=INFINITY)
                    {
                        visited[j] = TRUE;
                        printf("%c", G.vexs[j]);
                        EnQueue(Q, j);
                    }
                }
            }
        }
    }
}


typedef struct ArcNode//定义表节点
{
	int adjvex;//该弧所指的顶点位置
	struct ArcNode *nextarc;//指向下一条弧的指针
	InfoType *info;//该弧相关信息的指针
}ArcNode;

typedef struct Vnode//定义头结点
{
	VertexType data;//顶点的信息
	ArcNode *firstarc;//指向第一条依附于该顶点的弧的指针
}VNode,AdjList[MAX_VERTEX_NUM];//头结点是个一维j结构体数组

typedef struct//定义邻接表
{
	AdjList vertices;//头结点的一维数组
	int vexnum,arcnum;//图的当前顶点数和弧数
	int kind;//图的种类
}ALGraph;

void CreatAlgraph(ALGraph &G)//创建以邻接表为存储结构的无向图
{
	  int n,e;
	  printf("请输入顶点和边的数目");
	  scanf("%d%d",&G.vexnum,&G.arcnum);//读入顶点的数目
      n=G.vexnum; e=G.arcnum;
      printf("请依次输入顶点的值");
      for(int i=1;i<=n;i++) //读入顶点值
      {
	   scanf(" %c",&G.vertices[i].data);
	   G.vertices[i].firstarc=NULL;
	    }
       printf("请依次输入有关联的结点序号");
      for(int i=1;i<=e;i++)//建立边<s,d>(<d,s>)的信息
      {
			int s,d;
			ArcNode*p,*q;
	        scanf("%d%d",&s,&d);//读入顶点序号
            p=(ArcNode*)malloc(sizeof(ArcNode));//建立表节点
            q=(ArcNode*)malloc(sizeof(ArcNode));
            p->adjvex=d;//给弧所指的节点赋值
	        q->adjvex=s;
            p->nextarc=G.vertices[s].firstarc;G.vertices [s].firstarc=p;//这是一种逆向建立链表的方法
            q->nextarc=G.vertices[d].firstarc;G.vertices[d].firstarc=q;//<s,d>
    }
   
    
}

Status Print(int i,ALGraph G)
{
	printf("%c",G. vertices[i].data);
	return OK;
}


//临接表的广度优先遍历
void BFSTraverse1( ALGraph G, Status ( * Visit) (int v,ALGraph))
{
	int v,u;
	ArcNode *w;
	Visit=Print;
     for ( v=1; v <=G.vexnum; ++v )
     visited[v] = FALSE;
	 SqQueue Q;
	 InitQueue(Q);     //初始化
     for ( v=1; v <=G.vexnum; ++v )
          if ( ! visited[ v ] ) //v没有被访问
        {
		     visited[v] = TRUE;
			 Visit(v,G);
             EnQueue(Q,v);  //入队列
	         while (! QueueEmpty (Q))
	        {
			     DeQueue(Q,u);
				 w = G.vertices[u].firstarc;

				 for ( w = G.vertices[u].firstarc; w ; w =w->nextarc  )
             	 if( !visited[ w->adjvex ] )  //w为u的尚未访问的邻接顶点
	     	         {
					     visited[ w->adjvex ] = TRUE;
						    Visit(w->adjvex,G);
						 EnQueue(Q, w->adjvex);
			         }//if ;
            } //while
        }//if
}//BFSTraverse



 Status (*VisitFunc)(int v,ALGraph G);
 
 void DFS1( ALGraph G, int v )
 {
    
	  ArcNode *w;
      visited[v] = true;
      	 VisitFunc(v,G);
      for (w = G.vertices[v].firstarc; w ; w =w->nextarc )
           if ( ! visited[ w->adjvex ] )  DFS1(G,w->adjvex );  //对v的尚未访问的邻接顶点w调用DFS
 }
 //对于全局变量,如果在函数中修改了值,则全局变量的值就会被改变,在其他函数中访问的就是修改后的值;
//但是,如果在函数中定义了跟全局变量名称一样的局部变量,则函数中修改的是局部变量,全局变量的值是无法被修改的。
 void DFSTraverse1( ALGraph G)
 {     int v;
      VisitFunc = Print;   //使用全局变量VisitFunc,使DFS不必设函数指针参数
      for ( v=1; v <G.vexnum; ++v )  visited[v] = FALSE;
      for ( v=1; v <G.vexnum; ++v )
            if ( ! visited[ v ] ) DFS1(G, v  );//对每个未被访问的顶点调用DFS
 }



int main()
{
ALGraph G;
//	 DFSTraverse(G);
//	 BFSTraverse(G);

CreatAlgraph(G);
printf("深度优先遍历的序列:");
DFSTraverse1( G);
	 return 0;
}

效果展示
在这里插入图片描述
头文件代码
#include"queuevex.h"


#define STACK_INIT_SIZE 100
#define MAXQSIZE 100

typedef int QElemType;

typedef struct
{
    QElemType *base;
	int front;
	int rear;
}SqQueue;

Status QueueEmpty(SqQueue Q)//栈空返回1,不空返回0
{
     if(Q.rear==Q.front)
	 return OK;
	 else return ERROR;
}
Status InitQueue(SqQueue &Q)
{
	Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
	if(!Q.base) return OVERFLOW;
    Q.front=Q.rear=0;
	return OK;
}

int QueneLength(SqQueue Q)

{
	return(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}

Status EnQueue (SqQueue &Q,QElemType e)
{
	if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
	Q.base[Q.rear]=e;
	Q.rear=(Q.rear+1)%MAXQSIZE;
	return OK;
}

Status DeQueue(SqQueue &Q,QElemType &e)
{
	if(Q.front==Q.rear) return ERROR;
	e=Q.base[Q.front];
	Q.front=(Q.front+1)%MAXQSIZE;
	return OK;

}

  • 4
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 邻接表邻接矩阵的两种常见表示方法。下面分别给出邻接表邻接矩阵实现广度遍历深度遍历的代码。 ### 邻接表 ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 100 // 中最大顶点数 // 邻接表中表示边的结构体 typedef struct EdgeNode { int adjvex; // 邻接点在数组中的位置下标 struct EdgeNode *next; // 指向下一个邻接点的指针 } EdgeNode; // 邻接表中表示顶点的结构体 typedef struct VertexNode { int data; // 顶点的数据 EdgeNode *firstedge; // 指向第一个邻接点的指针 } VertexNode, AdjList[MAX_VERTEX_NUM]; // 定义结构体 typedef struct Graph { AdjList adjList; // 邻接表 int vexnum, arcnum; // 顶点数和边数 } Graph; // 初始化邻接表 void InitGraph(Graph *G) { G->vexnum = G->arcnum = 0; for (int i = 0; i < MAX_VERTEX_NUM; i++) { G->adjList[i].data = 0; G->adjList[i].firstedge = NULL; } } // 添加边 void AddEdge(Graph *G, int u, int v) { EdgeNode *p = (EdgeNode *)malloc(sizeof(EdgeNode)); p->adjvex = v; p->next = G->adjList[u].firstedge; G->adjList[u].firstedge = p; } // 创建 void CreateGraph(Graph *G) { printf("请输入顶点数和边数:"); scanf("%d %d", &G->vexnum, &G->arcnum); printf("请输入每个顶点的数据:"); for (int i = 0; i < G->vexnum; i++) { scanf("%d", &G->adjList[i].data); } printf("请输入每条边的起点和终点:"); for (int i = 0; i < G->arcnum; i++) { int u, v; scanf("%d %d", &u, &v); AddEdge(G, u, v); AddEdge(G, v, u); // 无向图需要加上反向边 } } // 访问顶点 void Visit(int v) { printf("%d ", v); } // 广度遍历 void BFS(Graph *G, int v) { int visited[MAX_VERTEX_NUM] = {0}; // 标记是否被访问过 int queue[MAX_VERTEX_NUM], front = 0, rear = 0; // 队列 Visit(v); visited[v] = 1; queue[rear++] = v; while (front != rear) { int u = queue[front++]; for (EdgeNode *p = G->adjList[u].firstedge; p != NULL; p = p->next) { int w = p->adjvex; if (!visited[w]) { Visit(w); visited[w] = 1; queue[rear++] = w; } } } } // 深度遍历 void DFS(Graph *G, int v, int visited[]) { Visit(v); visited[v] = 1; for (EdgeNode *p = G->adjList[v].firstedge; p != NULL; p = p->next) { int w = p->adjvex; if (!visited[w]) { DFS(G, w, visited); } } } int main() { Graph G; InitGraph(&G); CreateGraph(&G); printf("广度遍历:"); BFS(&G, 0); printf("\n深度遍历:"); int visited[MAX_VERTEX_NUM] = {0}; DFS(&G, 0, visited); return 0; } ``` ### 邻接矩阵 ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 100 // 中最大顶点数 // 定义结构体 typedef struct Graph { int vexnum, arcnum; // 顶点数和边数 int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 邻接矩阵 } Graph; // 初始化邻接矩阵 void InitGraph(Graph *G) { G->vexnum = G->arcnum = 0; for (int i = 0; i < MAX_VERTEX_NUM; i++) { for (int j = 0; j < MAX_VERTEX_NUM; j++) { G->arcs[i][j] = 0; } } } // 添加边 void AddEdge(Graph *G, int u, int v) { G->arcs[u][v] = 1; G->arcs[v][u] = 1; // 无向图需要加上反向边 } // 创建 void CreateGraph(Graph *G) { printf("请输入顶点数和边数:"); scanf("%d %d", &G->vexnum, &G->arcnum); printf("请输入每个顶点的数据:"); for (int i = 0; i < G->vexnum; i++) { for (int j = 0; j < G->vexnum; j++) { G->arcs[i][j] = 0; } scanf("%*d"); } printf("请输入每条边的起点和终点:"); for (int i = 0; i < G->arcnum; i++) { int u, v; scanf("%d %d", &u, &v); AddEdge(G, u, v); } } // 访问顶点 void Visit(int v) { printf("%d ", v); } // 广度遍历 void BFS(Graph *G, int v) { int visited[MAX_VERTEX_NUM] = {0}; // 标记是否被访问过 int queue[MAX_VERTEX_NUM], front = 0, rear = 0; // 队列 Visit(v); visited[v] = 1; queue[rear++] = v; while (front != rear) { int u = queue[front++]; for (int w = 0; w < G->vexnum; w++) { if (G->arcs[u][w] && !visited[w]) { Visit(w); visited[w] = 1; queue[rear++] = w; } } } } // 深度遍历 void DFS(Graph *G, int v, int visited[]) { Visit(v); visited[v] = 1; for (int w = 0; w < G->vexnum; w++) { if (G->arcs[v][w] && !visited[w]) { DFS(G, w, visited); } } } int main() { Graph G; InitGraph(&G); CreateGraph(&G); printf("广度遍历:"); BFS(&G, 0); printf("\n深度遍历:"); int visited[MAX_VERTEX_NUM] = {0}; DFS(&G, 0, visited); return 0; } ``` ### 回答2: 广度遍历深度遍历的两种主要遍历方式。C语言编程中,我们可以用邻接表邻接矩阵实现这两种遍历邻接表是一种常见的的表示方法,它通过链表来表示中的每个顶点以及与其相邻的顶点。广度遍历使用队列实现深度遍历使用递归实现邻接矩阵是另一种常见的的表示方法,它使用二维数组来表示的连接情况。广度遍历使用队列实现深度遍历使用栈或递归实现。 对于广度遍历实现,我们可以按照以下步骤进行: 1. 创建一个队列,并将起始顶点放入队列中。 2. 初始化一个数组visited,用来标记顶点是否被访问过,初始值为0(未访问)。 3. 当队列非空时,执行以下操作: 3.1 出队一个顶点v,并将其标记为已访问。 3.2 访问顶点v,并进行相关操作。 3.3 将v的所有未访问的邻居顶点入队。 对于深度遍历实现,我们可以按照以下步骤进行: 1. 创建一个栈,并将起始顶点放入栈中。 2. 初始化一个数组visited,用来标记顶点是否被访问过,初始值为0(未访问)。 3. 当栈非空时,执行以下操作: 3.1 出栈一个顶点v,并将其标记为已访问。 3.2 访问顶点v,并进行相关操作。 3.3 将v的所有未访问的邻居顶点入栈。 以上是用邻接表邻接矩阵实现广度遍历深度遍历的基本思路。具体实现时,我们需要根据具体的数据结构实现相应的操作,比如针对邻接表的创建、节点插入等操作,以及邻接矩阵的创建、二维数组的遍历等操作。 ### 回答3: 邻接表邻接矩阵是常用的存储结构。它们可以用于实现广度优先遍历(BFS)和深度优先遍历(DFS)算法。 邻接表是由中每个顶点的所有邻接顶点构成的链表。对于无向图邻接表是一个无序链表;对于有向邻接表是一个有序链表。我们可以使用一个数组来表示邻接表,数组的每个位置存储一个链表,链表的节点表示邻接顶点。 邻接矩阵是一个二维数组,行列分别表示中的顶点,矩阵中的元素表示两个顶点之间是否有边。如果有边,则为1或表示边的权重;如果没有边,则为0。邻接矩阵可以使用二维数组来表示。 对于广度优先遍历算法,我们可以使用一个队列来辅助进行遍历。首先将起始节点放入队列中,然后循环以下步骤:从队列中取出一个节点,遍历该节点的邻接顶点,并将未访问的邻接顶点依次放入队列中。直到队列为空为止。 对于深度优先遍历算法,我们可以使用递归或者栈来辅助进行遍历。首先将起始节点标记为已访问,然后循环以下步骤:选择一个未访问的邻接顶点,递归地对该顶点进行深度优先遍历。直到所有的顶点都被访问为止。 使用邻接表邻接矩阵实现广度遍历深度遍历,核心思想是遍历中的每个顶点,并按照特定的算法顺序访问和处理顶点。具体实现细节可以根据具体需求和数据结构选择适合的方式。 总之,使用邻接表邻接矩阵可以很方便地实现广度遍历深度遍历,这两种方法适用于不同的场景和需求,可以根据具体情况进行选择和实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值